#!/usr/bin/env python3  /*设定脚本文件直接能执行*/
# -*- coding:utf-8 -*-   /*设置编码*/
"""
@Author: zhuo
@Software: PyCharm
@File: tCrcCheck.py
@Time: 2023/9/18 13:33
@Function： CRC校验通用模块
"""
import crcmod
import binascii


# <editor-fold desc="CRC16/MODBUS校验">
# CRC16/MODBUS校验
def f_crc16ModbusFunc(verity_data):
    """
    function:  CRC16/MODBUS校验，多项式0x8005，初始值0xFFFF，结果异或值0x0000
          in:  verity_data：需要校验的数据帧 十六进制bytes类型
         out:  CRC16校验，返回十进制，高位在前
      return:  int
      others:  CRC16/MODBUS Check
    """
    if not verity_data:
        return
    crc16_modbus = crcmod.mkCrcFun(0x18005, rev=True, initCrc=0xFFFF, xorOut=0x0000)
    return crc16_modbus(verity_data)


# 校验数据帧的CRC码是否正确
def f_checkCrcFunc(verity_data):
    """
    function:  校验数据帧的CRC16/MODBUS码是否正确
          in:  verityData：需要校验的数据帧 十六进制bytes类型
         out:  bool
      return:  true：校验码正确 false：校验码错误
      others:  Verify that the CRC16/MODBUS code of the data frame is correct
    """
    if not verity_data:
        return False
    if len(verity_data) <= 2:
        return False
    # 数据，截取到倒数第3位
    no_crc_data = verity_data[:-2]
    # 校验码（后2字节）
    old_crc16 = verity_data[-2:]
    old_crc_list = list(old_crc16)
    # 计算CRC结果
    crc_res = f_crc16ModbusFunc(no_crc_data)
    # 将计算的CRC结果转换数据类型
    crc16_bytes = crc_res.to_bytes(2, byteorder="little", signed=False)
    crc_list = list(crc16_bytes)
    if old_crc_list[0] != crc_list[0] or old_crc_list[1] != crc_list[1]:
        return False
    return True


# CRC校验 报文拼接 高位在前
def f_crcJoinFunc(str_send):
    """
    function:  CRC校验 报文拼接  高位在前
          in:  str_send：输入字符串格式的报文
         out:  hex_text：完整报文 十六进制bytes
               send_crc：CRC校验 bytes格式
               send_text：输入报文字符串去空格
      return:  int >0 ok, <0 some wrong
      others:  CRC Check Packet Concatenation Func
    """
    # 输入报文字符串去空格
    send_text = str_send.replace(" ", "")
    # 输入报文字符串转为bytes格式
    send_content_bytes = send_text.encode("utf-8")
    # bytes转十六进制bytes
    send_content = binascii.unhexlify(send_content_bytes)
    # int类型的CRC校验和
    crc = f_crc16ModbusFunc(send_content)
    # CRC校验 十进制转十六进制 转大写 补0
    hex_crc = hex(crc)[2:].upper().zfill(4)
    # 转bytes格式
    send_crc = hex_crc.encode("utf-8")
    # 拼接发送报文 bytes格式 完整报文
    send_content_bytes += send_crc
    # a2b_hex
    hex_text = binascii.unhexlify(send_content_bytes)
    return hex_text, send_crc, send_text


# CRC校验 报文拼接 低位在前
def f_crcJoinLowFun(str_send):
    """
    function:  CRC校验 报文拼接  低位在前
          in:  str_send：输入字符串格式的报文
         out:  hex_text：完整报文 十六进制bytes
               send_crc：CRC校验 bytes格式
               send_text：输入报文字符串去空格
      return:  int >0 ok, <0 some wrong
      others:  CRC Check Packet Concatenation Func
    """
    # 输入报文字符串去空格
    send_text = str_send.replace(" ", "")
    # 输入报文字符串转为bytes格式
    send_content_bytes = send_text.encode("utf-8")
    # bytes转十六进制bytes
    send_content = binascii.unhexlify(send_content_bytes)
    # int类型的CRC校验和
    crc = f_crc16ModbusFunc(send_content)
    # CRC校验 十进制转十六进制 转大写 补0
    hex_crc = hex(crc)[2:].upper().zfill(4)
    hex_crc = hex_crc[2:4] + hex_crc[0:2]
    # 转bytes格式
    send_crc = hex_crc.encode("utf-8")
    # 拼接发送报文 bytes格式 完整报文
    send_content_bytes += send_crc
    # a2b_hex
    hex_text = binascii.unhexlify(send_content_bytes)
    return hex_text, send_crc, send_text


# 获取报文data的CRC校验结果：依次返回低、高位十六进制校验结果   lzg 230810
def f_crcResultGetFunc(verity_data):
    """
    function:  获取报文data的CRC校验结果：依次返回低、高位十六进制校验结果
          in:  send_text：需要校验的数据帧 十六进制bytes类型
         out:  返回低、高位十六进制校验结果；如：('0x6e', '0x1f')
      return:  reg_crc_low,reg_crc_hige
      others:  Get CRC Check Result Of The Packet Data
    """
    length = len(verity_data)
    reg_crc = 0xFFFF
    for i in range(0, length):
        length -= 1
        reg_crc ^= verity_data[i]
        for j in range(8):
            if reg_crc & 0x01:
                reg_crc = (reg_crc >> 1) ^ 0xA001
            else:
                reg_crc >>= 1
    reg_crc_low = reg_crc & 0xff
    reg_crc_high = (reg_crc >> 8) & 0xff
    return hex(reg_crc_low), hex(reg_crc_high)


# </editor-fold>


# <editor-fold desc="CRC16/CCITT校验">
# CRC16/CCITT校验
def f_crc16CCITTFunc(verity_data):
    """
    function:  CRC16/CCITT校验，多项式1021，初始值:0xFFFF，结果异或值0x0000,高位在左低位在右
          in:  verity_data：需要校验的数据帧,字符串形式
         out:  CRC16 CCITT，返回字符串形式
      return:
      others:  CRC16/MODBUS Check Func
    """
    verity_data = str(verity_data)
    crc16_ccitt = crcmod.mkCrcFun(0x11021, rev=False, initCrc=0xFFFF, xorOut=0x0000)
    return __f_getCrcValueFunc(verity_data, crc16_ccitt)


# CRC16/CCITT校验的具体过程
def __f_getCrcValueFunc(data_crc, crc16_ccitt):
    """
    function:  CRC16/CCITT校验的具体过程
          in:  data_crc：需要校验的数据帧,字符串形式
               crc16_ccitt：CRC16 CCITT校验对象
         out:  CRC16 CCITT，返回字符串形式
      return:
      others:  The Process Of CRC16/CCITT Check Func
    """
    verity_data = data_crc.replace(' ', '')
    crc_out = hex(crc16_ccitt(binascii.unhexlify(verity_data))).upper()
    str_list = list(crc_out)
    if len(str_list) == 5:
        # 位数不足补0
        str_list.insert(2, '0')
    crc_data = ''.join(str_list[2:])
    # 2025.02.21：增加了补0的操作 .zfill(4)
    return (crc_data[:2] + crc_data[2:]).upper().zfill(4)


# 校验数据帧的CRC码是否正确
def f_checkCCITTCrcFunc(verity_data):
    """
    function:  校验数据帧的CRC16/MODBUS码是否正确
          in:  verity_data：需要校验的数据帧（字符串格式）
         out:  None
      return:  True：校验码正确 False：校验码错误
      others:  Check If The CRC16/MODBUS Code Of The Data Frame Is Correct Func
    """
    if len(verity_data) < 4:
        return False
    verity_data = verity_data.replace(' ', '')
    # 数据，截取到倒数第4位
    no_crc_data = verity_data[:-4]
    # 校验码（后2个字节）
    old_crc16 = verity_data[-4:]
    # 计算CRC结果
    crc_res = f_crc16CCITTFunc(no_crc_data)
    if crc_res == old_crc16:
        return True
    else:
        return False


# </editor-fold>


if __name__ == '__main__':
    data = '46 54 44 80 18 06'
    print(type(f_crc16CCITTFunc(data)), f_crc16CCITTFunc(data))
    print(f_checkCCITTCrcFunc("46 54 44 80 18 06 00 FE"))
    data = '46 54 08 42 42'
    print(type(f_crc16CCITTFunc(data)), f_crc16CCITTFunc(data))
    print(f_checkCCITTCrcFunc("46 54 08 42 42 D8 A2"))
